Return to start page
Systems/World/Struct Routine.j
1 library AStructSystemsWorldRoutine requires optional ALibraryCoreDebugMisc, AStructCoreGeneralHashTable
2
3 /*
4 /// Isn't a method since it uses @function TriggerSleepAction.
5 function moveNpcTo takes ARoutine routine, real x, real y returns boolean
6 local unit npc = routine.unit()
7 loop
8 if (not routine.isInTime()) then
9 set npc = null
10 return false
11 elseif (routine.isAbleToMove()) then
12 call IssuePointOrder(npc, "move", x, y)
13 exitwhen (true)
14 endif
15 call TriggerSleepAction(1.0)
16 endloop
17 set npc = null
18 return routine.isInTime()
19 endfunction
20
21 /// Isn't a method since it uses @function TriggerSleepAction.
22 function moveNpcToRectCenter takes ARoutine routine, rect targetRect returns boolean
23 return moveNpcTo(routine, GetRectCenterX(targetRect), GetRectCenterY(targetRect))
24 endfunction
25
26 /// Isn't a method since it uses @function TriggerSleepAction.
27 function waitForNewNpcRect takes ARoutine routine, rect newRect returns boolean
28 local unit npc = routine.unit()
29 loop
30 exitwhen (RectContainsUnit(newRect, npc) or not routine.isInTime())
31 call TriggerSleepAction(1.0)
32 endloop
33 set npc = null
34 return routine.isInTime()
35 endfunction
36
37 /// Isn't a method since it uses @function TriggerSleepAction.
38 function moveNpcToRectCenterAndWait takes ARoutine routine, rect targetRect returns boolean
39 if (not moveNpcToRectCenter(routine, targetRect)) then
40 return false
41 endif
42 if (not waitForNewNpcRect(routine, targetRect)) then
43 return false
44 endif
45 return routine.isInTime()
46 endfunction
47 */
48
49 struct ARoutineUnitData
50 //start members
51 private ARoutine m_routine
52 private unit m_unit
53 private real m_startTimeOfDay
54 private real m_endTimeOfDay
55 private rect m_targetRect
56 //members
57 private boolean m_isEnabled
58 private trigger m_startTrigger
59 private trigger m_endTrigger
60 private trigger m_targetTrigger
61 private region m_targetRegion
62
63 //! runtextmacro optional A_STRUCT_DEBUG("\"ARoutineUnitData\"")
64
65 public method routine takes nothing returns ARoutine
66 return this.m_routine
67 endmethod
68
69 public method unit takes nothing returns unit
70 return this.m_unit
71 endmethod
72
73 public method startTimeOfDay takes nothing returns real
74 return this.m_startTimeOfDay
75 endmethod
76
77 public method endTimeOfDay takes nothing returns real
78 return this.m_endTimeOfDay
79 endmethod
80
81 public method targetRect takes nothing returns rect
82 return this.m_targetRect
83 endmethod
84
85 //members
86
87 public method isEnabled takes nothing returns boolean
88 return this.m_isEnabled
89 endmethod
90
91 //methods
92
93 public method enable takes nothing returns nothing
94 set this.m_isEnabled = true
95 call EnableTrigger(this.m_startTrigger)
96 if (not IsUnitPaused(this.m_unit)) then
97 call EnableTrigger(this.m_endTrigger)
98 if (this.m_targetTrigger != null) then
99 call EnableTrigger(this.m_targetTrigger)
100 endif
101 endif
102 endmethod
103
104 public method disable takes nothing returns nothing
105 call BJDebugMsg("Disable routine of " + GetUnitName(this.m_unit))
106 set this.m_isEnabled = false
107 call DisableTrigger(this.m_startTrigger)
108 if (not IsUnitPaused(this.m_unit)) then
109 call DisableTrigger(this.m_endTrigger)
110 if (this.m_targetTrigger != null) then
111 call DisableTrigger(this.m_targetTrigger)
112 endif
113 endif
114 endmethod
115
116 public method setEnabled takes boolean enabled returns nothing
117 if (enabled) then
118 call this.enable()
119 else
120 call this.disable()
121 endif
122 endmethod
123
124 public method isInTime takes nothing returns boolean
125 if (this.m_endTimeOfDay < this.m_startTimeOfDay) then //next day
126 return GetFloatGameState(GAME_STATE_TIME_OF_DAY) >= this.m_startTimeOfDay or GetFloatGameState(GAME_STATE_TIME_OF_DAY) <= this.m_startTimeOfDay
127 endif
128 return GetFloatGameState(GAME_STATE_TIME_OF_DAY) >= this.m_startTimeOfDay and GetFloatGameState(GAME_STATE_TIME_OF_DAY) <= this.m_endTimeOfDay
129 endmethod
130
131 private static method filterTarget takes nothing returns boolean
132 local trigger triggeringTrigger = GetTriggeringTrigger()
133 local thistype this = AHashTable.global().handleInteger(triggeringTrigger, "this")
134 local unit triggerUnit = GetTriggerUnit()
135 local boolean result = triggerUnit == this.m_unit
136 set triggeringTrigger = null
137 set triggerUnit = null
138 return result
139 endmethod
140
141 private static method triggerActionTarget takes nothing returns nothing
142 local trigger triggeringTrigger = GetTriggeringTrigger()
143 local thistype this = AHashTable.global().handleInteger(triggeringTrigger, "this")
144 call this.destroyTargetTrigger() // destroys this trigger
145 if (this.m_routine.targetAction() != 0) then
146 call this.m_routine.targetAction().execute(this)
147 endif
148 set triggeringTrigger = null
149 endmethod
150
151 private method createTargetTrigger takes nothing returns nothing
152 local boolexpr filter
153 local event triggerEvent
154 local triggeraction triggerAction
155 set this.m_targetRegion = CreateRegion()
156 call RegionAddRect(this.m_targetRegion, this.m_targetRect)
157 set this.m_targetTrigger = CreateTrigger()
158 set filter = Condition(function thistype.filterTarget)
159 set triggerEvent = TriggerRegisterEnterRegion(this.m_targetTrigger, this.m_targetRegion, filter)
160 set triggerAction = TriggerAddAction(this.m_targetTrigger, function thistype.triggerActionTarget)
161 call AHashTable.global().setHandleInteger(this.m_targetTrigger, "this", this)
162 call IssuePointOrder(this.m_unit, "move", GetRectCenterX(this.m_targetRect), GetRectCenterY(this.m_targetRect))
163 set filter = null
164 set triggerEvent = null
165 set triggerAction = null
166 endmethod
167
168 private static method triggerActionStart takes nothing returns nothing
169 local trigger triggeringTrigger = GetTriggeringTrigger()
170 local thistype this = AHashTable.global().handleInteger(triggeringTrigger, "this")
171 call BJDebugMsg("Start trigger is called")
172 if (not IsUnitPaused(this.m_unit)) then
173 call BJDebugMsg("Not paused")
174 if (thistype.unitHasNextRoutineUnitData(this.m_unit)) then
175 call thistype.clearNextRoutineUnitDataOfUnit(this.m_unit)
176 endif
177 call thistype.setCurrentRoutineUnitDataForUnit(this.m_unit, this)
178 if (this.m_routine.startAction() != 0) then
179 call this.m_routine.startAction().execute(this)
180 endif
181 if (this.m_routine.hasTarget()) then
182 if (RectContainsUnit(this.m_targetRect, this.m_unit)) then
183 if (this.m_routine.targetAction() != 0) then
184 call this.m_routine.targetAction().execute(this)
185 endif
186 else
187 call this.createTargetTrigger()
188 endif
189 endif
190 else
191 call BJDebugMsg("Paused")
192 call thistype.setNextRoutineUnitDataForUnit(this.m_unit, this)
193 endif
194 set triggeringTrigger = null
195 endmethod
196
197 private method createStartTrigger takes nothing returns nothing
198 local event triggerEvent
199 local triggeraction triggerAction
200 set this.m_startTrigger = CreateTrigger()
201 set triggerEvent = TriggerRegisterGameStateEvent(this.m_startTrigger, GAME_STATE_TIME_OF_DAY, EQUAL, this.m_startTimeOfDay)
202 set triggerAction = TriggerAddAction(this.m_startTrigger, function thistype.triggerActionStart)
203 call AHashTable.global().setHandleInteger(this.m_startTrigger, "this", this)
204 set triggerEvent = null
205 set triggerAction = null
206 endmethod
207
208 private static method triggerActionEnd takes nothing returns nothing
209 local trigger triggeringTrigger = GetTriggeringTrigger()
210 local thistype this = AHashTable.global().handleInteger(triggeringTrigger, "this")
211 call thistype.clearCurrentRoutineUnitDataOfUnit(this.m_unit)
212 if (this.m_routine.hasTarget() and this.m_targetTrigger != null) then
213 call this.destroyTargetTrigger()
214 endif
215 if (this.m_routine.endAction() != 0) then
216 call this.m_routine.endAction().execute(this)
217 endif
218 set triggeringTrigger = null
219 endmethod
220
221 private method createEndTrigger takes nothing returns nothing
222 local event triggerEvent
223 local triggeraction triggerAction
224 set this.m_endTrigger = CreateTrigger()
225 set triggerEvent = TriggerRegisterGameStateEvent(this.m_endTrigger, GAME_STATE_TIME_OF_DAY, EQUAL, this.m_endTimeOfDay)
226 set triggerAction = TriggerAddAction(this.m_endTrigger, function thistype.triggerActionEnd)
227 call AHashTable.global().setHandleInteger(this.m_endTrigger, "this", this)
228 set triggerEvent = null
229 set triggerAction = null
230 endmethod
231
232 public static method create takes ARoutine routine, unit whichUnit, real startTimeOfDay, real endTimeOfDay, rect targetRect returns thistype
233 local thistype this = thistype.allocate()
234 //start members
235 set this.m_routine = routine
236 set this.m_unit = whichUnit
237 set this.m_startTimeOfDay = startTimeOfDay
238 set this.m_endTimeOfDay = endTimeOfDay
239 set this.m_targetRect = targetRect
240 //members
241 set this.m_isEnabled = true
242 set this.m_targetRegion = null
243
244 call this.createStartTrigger()
245 call this.createEndTrigger()
246
247 return this
248 endmethod
249
250 private method destroyTargetTrigger takes nothing returns nothing
251 call AHashTable.global().destroyTrigger(this.m_targetTrigger)
252 set this.m_targetTrigger = null
253 call RemoveRegion(this.m_targetRegion)
254 set this.m_targetRegion = null
255 endmethod
256
257 public method onDestroy takes nothing returns nothing
258 //start members
259 set this.m_unit = null
260 set this.m_targetRect = null
261 //members
262 call AHashTable.global().destroyTrigger(this.m_startTrigger)
263 set this.m_startTrigger = null
264 call AHashTable.global().destroyTrigger(this.m_endTrigger)
265 set this.m_endTrigger = null
266 if (this.m_targetTrigger != null) then
267 call this.destroyTargetTrigger()
268 endif
269 endmethod
270
271 public static method hookPauseUnit takes unit whichUnit, boolean flag returns nothing
272 local thistype this
273
274 if (not thistype.unitHasCurrentRoutineUnitData(whichUnit)) then
275 return
276 endif
277
278 set this = thistype.currentRoutineUnitDataOfUnit(whichUnit)
279
280 debug if (this == 0) then
281 debug call thistype.staticPrint("Current routine unit data of unit " + GetUnitName(whichUnit) + " is 0.")
282 debug return
283 debug endif
284
285 if (not this.m_isEnabled) then
286 debug call this.print("Is not enabled.")
287 return
288 endif
289
290 // pause
291 if (flag) then
292 call BJDebugMsg("Routine Pause " + GetUnitName(whichUnit))
293 call DisableTrigger(this.m_endTrigger)
294 if (this.m_targetTrigger != null) then
295 call DisableTrigger(this.m_targetTrigger)
296 endif
297 // unpause
298 else
299 call BJDebugMsg("Routine Unpause " + GetUnitName(whichUnit))
300 if (this.isInTime()) then
301 call BJDebugMsg("Is in time")
302 if (this.m_routine.hasTarget()) then
303 if (this.m_targetTrigger != null) then
304 call EnableTrigger(this.m_endTrigger)
305 if (this.m_targetTrigger != null) then
306 call EnableTrigger(this.m_targetTrigger)
307 endif
308 call IssuePointOrder(this.m_unit, "move", GetRectCenterX(this.m_targetRect), GetRectCenterY(this.m_targetRect))
309 elseif (this.m_routine.isLoop() and this.m_routine.targetAction() != 0) then
310 call this.m_routine.targetAction().execute(this)
311 endif
312 endif
313 // not in time
314 else
315 call BJDebugMsg("Is not in time")
316 if (not thistype.unitHasNextRoutineUnitData(this.m_unit)) then
317 return
318 endif
319
320 set this = thistype.nextRoutineUnitDataOfUnit(whichUnit)
321 call thistype.clearNextRoutineUnitDataOfUnit(whichUnit)
322 call thistype.setCurrentRoutineUnitDataForUnit(whichUnit, this)
323 if (this.m_routine.startAction() != 0) then
324 call this.m_routine.startAction().execute(this)
325 endif
326 endif
327 endif
328 endmethod
329
330 public static method hookRemoveUnit takes unit whichUnit returns nothing
331 call ARoutine.removeUnitFromAll(whichUnit)
332 if (thistype.unitHasCurrentRoutineUnitData(whichUnit)) then
333 call thistype.clearCurrentRoutineUnitDataOfUnit(whichUnit)
334 endif
335 if (thistype.unitHasNextRoutineUnitData(whichUnit)) then
336 call thistype.clearNextRoutineUnitDataOfUnit(whichUnit)
337 endif
338 endmethod
339
340 public static method enableCurrentRoutineUnitDataOfUnit takes unit whichUnit returns boolean
341 local boolean result = thistype.unitHasCurrentRoutineUnitData(whichUnit)
342 if (result) then
343 call thistype.currentRoutineUnitDataOfUnit(whichUnit).enable()
344 endif
345 return result
346 endmethod
347
348 public static method disableCurrentRoutineUnitDataOfUnit takes unit whichUnit returns boolean
349 local boolean result = thistype.unitHasCurrentRoutineUnitData(whichUnit)
350 if (result) then
351 call thistype.currentRoutineUnitDataOfUnit(whichUnit).disable()
352 endif
353 return result
354 endmethod
355
356 public static method currentRoutineUnitDataOfUnit takes unit whichUnit returns thistype
357 return AHashTable.global().handleInteger(whichUnit, "ARoutineUnitData:currentRoutineUnitData")
358 endmethod
359
360 public static method unitHasCurrentRoutineUnitData takes unit whichUnit returns boolean
361 return AHashTable.global().hasHandleInteger(whichUnit, "ARoutineUnitData:currentRoutineUnitData")
362 endmethod
363
364 public static method nextRoutineUnitDataOfUnit takes unit whichUnit returns thistype
365 return AHashTable.global().handleInteger(whichUnit, "ARoutineUnitData:nextRoutineUnitData")
366 endmethod
367
368 public static method unitHasNextRoutineUnitData takes unit whichUnit returns boolean
369 return AHashTable.global().hasHandleInteger(whichUnit, "ARoutineUnitData:nextRoutineUnitData")
370 endmethod
371
372 private static method setCurrentRoutineUnitDataForUnit takes unit whichUnit, thistype routineUnitData returns nothing
373 call AHashTable.global().setHandleInteger(whichUnit, "ARoutineUnitData:currentRoutineUnitData", routineUnitData)
374 endmethod
375
376 private static method clearCurrentRoutineUnitDataOfUnit takes unit whichUnit returns nothing
377 call AHashTable.global().removeHandleInteger(whichUnit, "ARoutineUnitData:currentRoutineUnitData")
378 endmethod
379
380 private static method setNextRoutineUnitDataForUnit takes unit whichUnit, thistype routineUnitData returns nothing
381 //don't check if there's already a value, just overwrite!
382 call AHashTable.global().setHandleInteger(whichUnit, "ARoutineUnitData:nextRoutineUnitData", routineUnitData)
383 endmethod
384
385 private static method clearNextRoutineUnitDataOfUnit takes unit whichUnit returns nothing
386 call AHashTable.global().removeHandleInteger(whichUnit, "ARoutineUnitData:nextRoutineUnitData")
387 endmethod
388 endstruct
389
390 hook PauseUnit ARoutineUnitData.hookPauseUnit
391 hook RemoveUnit ARoutineUnitData.hookRemoveUnit
392
393 function AContinueRoutineLoop takes ARoutineUnitData routineUnitData, ARoutineAction routineAction returns nothing
394 if (not IsUnitPaused(routineUnitData.unit())) then
395 call routineAction.execute(routineUnitData)
396 endif
397 //otherwise cancel, routine loop action will be called automatically again when unit is unpaused and still in routine time
398 endfunction
399
400 /// @todo Should be a part of @struct ARoutine, vJass bug.
401 function interface ARoutineAction takes ARoutineUnitData routineUnitData returns nothing
402
403 /**
404 * Provides NPC routine handling like in the game Gothic or Gothic II.
405 * You are able to assign day times and routine actions using the function interface @functioninterface ARouteAction.
406 * Additionally you can make periodic routines by setting a 'run rate'.
407 * If the assigned unit is paused, routine won't be runned until unit gets unpaused.
408 */
409 struct ARoutine
410 //static members
411 private static AIntegerVector m_routines
412 //start members
413 private boolean m_hasTarget
414 private boolean m_isLoop
415 private ARoutineAction m_startAction
416 private ARoutineAction m_endAction
417 private ARoutineAction m_targetAction
418 //members
419 private AIntegerVector m_unitData
420 private integer m_index
421
422 //start members
423
424 public method hasTarget takes nothing returns boolean
425 return this.m_hasTarget
426 endmethod
427
428 public method isLoop takes nothing returns boolean
429 return this.m_isLoop
430 endmethod
431
432 public method startAction takes nothing returns ARoutineAction
433 return this.m_startAction
434 endmethod
435
436 public method endAction takes nothing returns ARoutineAction
437 return this.m_startAction
438 endmethod
439
440 public method targetAction takes nothing returns ARoutineAction
441 return this.m_targetAction
442 endmethod
443
444 //methods
445
446 public method addUnit takes unit whichUnit, real startTimeOfDay, real endTimeOfDay, rect targetRect returns integer
447 local ARoutineUnitData routineUnitData = ARoutineUnitData.create(this, whichUnit, startTimeOfDay, endTimeOfDay, targetRect)
448 call this.m_unitData.pushBack(routineUnitData)
449 return this.m_unitData.backIndex()
450 endmethod
451
452 public method enableForUnitByIndex takes integer index returns nothing
453 call ARoutineUnitData(this.m_unitData[index]).enable()
454 endmethod
455
456 public method enableForUnit takes unit whichUnit returns integer
457 local integer i = 0
458 loop
459 exitwhen (i == this.m_unitData.size())
460 if (ARoutineUnitData(this.m_unitData[i]).unit() == whichUnit) then
461 call this.enableForUnitByIndex(i)
462 return i
463 endif
464 set i = i + 1
465 endloop
466 return -1
467 endmethod
468
469 public method disableForUnitByIndex takes integer index returns nothing
470 call ARoutineUnitData(this.m_unitData[index]).disable()
471 endmethod
472
473 public method disableForUnit takes unit whichUnit returns integer
474 local integer i = 0
475 loop
476 exitwhen (i == this.m_unitData.size())
477 if (ARoutineUnitData(this.m_unitData[i]).unit() == whichUnit) then
478 call this.disableForUnitByIndex(i)
479 return i
480 endif
481 set i = i + 1
482 endloop
483 return -1
484 endmethod
485
486 public method removeUnitByIndex takes integer index returns nothing
487 call ARoutineUnitData(this.m_unitData[index]).destroy()
488 call this.m_unitData.erase(index)
489 endmethod
490
491 public method removeUnit takes unit whichUnit returns integer
492 local integer i = 0
493 loop
494 exitwhen (i == this.m_unitData.size())
495 if (ARoutineUnitData(this.m_unitData[i]).unit() == whichUnit) then
496 call this.removeUnitByIndex(i)
497 return i
498 endif
499 set i = i + 1
500 endloop
501 return -1
502 endmethod
503
504 public static method create takes boolean hasTarget, boolean isLoop, ARoutineAction startAction, ARoutineAction endAction, ARoutineAction targetAction returns thistype
505 local thistype this = thistype.allocate()
506 //start members
507 set this.m_hasTarget = hasTarget
508 set this.m_isLoop = isLoop
509 set this.m_startAction = startAction
510 set this.m_endAction = endAction
511 set this.m_targetAction = targetAction
512 //members
513 set this.m_unitData = AIntegerVector.create()
514 call thistype.m_routines.pushBack(this)
515 set this.m_index = thistype.m_routines.backIndex()
516
517 return this
518 endmethod
519
520 public method onDestroy takes nothing returns nothing
521 //members
522 loop
523 exitwhen (this.m_unitData.empty())
524 call this.removeUnitByIndex(this.m_unitData.backIndex())
525 endloop
526 call this.m_unitData.destroy()
527 call thistype.m_routines.erase(this.m_index)
528 endmethod
529
530 public static method init takes nothing returns nothing
531 //static members
532 set thistype.m_routines = AIntegerVector.create()
533 endmethod
534
535 public static method enableForUnitInAll takes unit whichUnit returns nothing
536 local integer i = 0
537 loop
538 exitwhen (i == thistype.m_routines.size())
539 call thistype(thistype.m_routines[i]).enableForUnit(whichUnit)
540 set i = i + 1
541 endloop
542 endmethod
543
544 public static method disableForUnitInAll takes unit whichUnit returns nothing
545 local integer i = 0
546 loop
547 exitwhen (i == thistype.m_routines.size())
548 call thistype(thistype.m_routines[i]).disableForUnit(whichUnit)
549 set i = i + 1
550 endloop
551 endmethod
552
553 public static method removeUnitFromAll takes unit whichUnit returns nothing
554 local integer i = 0
555 loop
556 exitwhen (i == thistype.m_routines.size())
557 call thistype(thistype.m_routines[i]).removeUnit(whichUnit)
558 set i = i + 1
559 endloop
560 endmethod
561 endstruct
562
563 endlibrary